﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using System.Threading;

namespace NGinnBPM.Services
{
    public class ResourceLockManager : IResourceLockManager
    {
        private static Logger log = LogManager.GetCurrentClassLogger();

        internal class RCLock : IResourceLock
        {
            internal string RCId { get; set; }
            internal ResourceLockManager Parent { get; set; }
            private DateTime _dt = DateTime.Now;

            public DateTime AcquiredDate
            {
                get { return _dt; }
            }

            public void  Release()
            {
                Parent.Release(RCId);
            }

            public void  Dispose()
            {
 	            Release();
            }
        }


        private Dictionary<string, RCLock> _locks = new Dictionary<string, RCLock>();
        private AutoResetEvent _waitQ = new AutoResetEvent(false);


        private void Release(string rcid)
        {
            lock (_locks)
            {
                bool b = _locks.Remove(rcid);
                if (b)
                    _waitQ.Set();
                else
                    log.Warn("No lock to release: {0}", rcid);
            }
            
        }


        public IResourceLock AcquireWriteLock(string rcId)
        {
            while (true)
            {
                lock (_locks)
                {
                    RCLock lk;
                    if (!_locks.TryGetValue(rcId, out lk))
                    {
                        lk = new RCLock { Parent = this, RCId = rcId };
                        _locks[rcId] = lk;
                        return lk;
                    }
                }
                _waitQ.WaitOne();
            }
        }

        public bool IsResourceLocked(string rcId)
        {
            return _locks.ContainsKey(rcId);
        }


        public IResourceLock AcquireWriteLockIfUnlocked(string rcId)
        {
            if (_locks.ContainsKey(rcId)) return null;
            lock (_locks)
            {
                RCLock lk;
                if (!_locks.TryGetValue(rcId, out lk))
                {
                    lk = new RCLock { Parent = this, RCId = rcId };
                    _locks[rcId] = lk;
                    return lk;
                }
                else
                {
                    return null;
                }
            }
        }
    }
}
